
#ifndef __CONNECTOR_H__
#define __CONNECTOR_H__

#include <vector>
#include <iostream>
#include <array>
#include <mutex>
#include <thread>
#include <chrono>

template<typename... DataTypes>
class connector;

template<>
class connector<>
{
public:
    connector(){}
    int read(){}
    int write(){}
};

// @brief ping pong structure RAM access class
// @param DataType: user define type whose copy constructor MUST BE implemented
template<typename DataType, typename... DataTypes>
class connector<DataType, DataTypes...> : public connector<DataTypes...>
{
    using base = connector<DataTypes...>;

public:
    
    connector() : base()
    {}
    
    // @brief copy data from the medium
    // @param data: user defined data to gain data
    int read(DataType& data, DataTypes&... datas){
        
        data_lk_.lock();
        int latest_modify_lock = latest_modify;
        if(latest_modify_lock == 0){
            mem0_occupying_++;
        }
        else{
            mem1_occupying_++;
        }
        data_lk_.unlock();
        
        // copy these volatiled datas just in case.
        if(latest_modify_lock == 0){
            data = mem0_data_;
        }
        else{
            data = mem1_data_;
        }
        // std::this_thread::sleep_for(std::chrono::milliseconds(10));
        data_lk_.lock();
        if(latest_modify_lock == 0){
            mem0_occupying_--;
        }
        else{
            mem1_occupying_--;
        }
        // std::cout << " read " << data <<" from memory " << latest_modify_lock << std::endl;
        data_lk_.unlock();
        
        static_cast<base*>(this)->read( datas...);
        
        return 0;
    }

    // @brief copy data to the medium
    // @param data: user defined data to be transfered
    int write(const DataType& data, const DataTypes&... datas){
        // cout << " copy write" << endl;
        int memory;
        while (true)
        {
            data_lk_.lock();
            if(latest_modify == 0 && mem1_occupying_ == 0)
            {
                memory = 1;
                data_lk_.unlock();
                break;
            }
            else if(latest_modify == 1 && mem0_occupying_ == 0)// memory1
            {
                memory = 0;
                data_lk_.unlock();
                break;
            }
            data_lk_.unlock();
            std::this_thread::sleep_for(std::chrono::milliseconds(2));
            // std::cout << "blocking for data writing, occupy mem0 " << mem0_occupying_ << " mem1 "<< mem1_occupying_<< std::endl;
        }
        if(memory){
            mem1_data_ = data;
            latest_modify = 1;
        }
        else{
            mem0_data_ = data;
            latest_modify = 0;
        }
        // std::cout << " write " << data <<" to memory " << memory << std::endl;
        // if constexpr(sizeof...datas > 0)
        static_cast<base*>(this)->write( datas...);
    }

    // int write(DataType&& data, const size_t id){
    //     cout << " move write" << endl;
    //     data_.at(id) = std::move(data);
    // }

    size_t size(void){
        return(sizeof...(DataTypes) + 1);
    }

private:
    DataType mem1_data_;
    int mem1_occupying_ = 0;
    DataType mem0_data_;
    int mem0_occupying_ = 0;
    std::mutex data_lk_;
    int latest_modify = 0;
};

template<typename... DataTypes>
using connector_ptr = std::shared_ptr<connector<DataTypes...>>;


#endif